#ifndef MCG_DATASET_H
#define MCG_DATASET_H

/*
 * API to listing files, frames etc. in a Multiple Camera Grab dataset.
 * Depends on Vision Base Lib for VBL_ASSERT, VBL_STR, VBL_SCAN, etc.
 */

#include <vbl/util/StringBuilder.h>

// DOUWE: ADDED
#include <boost/filesystem.hpp>
#include <vbl/Assert.h>
#include <iomanip>
//

namespace datasets {
namespace MCG {

namespace fs = boost::filesystem;

const int MCG_MAX_FILES_PER_SUBDIR = 1000;

// flags
enum MCG_CAM_FLAGS {

	CAM_0 = 1,
	CAM_1 = 2,
	CAM_2 = 4,
	CAM_3 = 8,
	
	CAM_ALL = CAM_0 | CAM_1 | CAM_2 | CAM_3,
	CAM_FIRST = CAM_0,

	CAM_ID_07 = CAM_0,
	CAM_ID_14 = CAM_1,
	CAM_ID_19 = CAM_2,
	CAM_ID_26 = CAM_3
};

inline
int ado_cam_to_flag(int cam) {
	return (1 << cam);
}

class MCGDataset
{
	fs::path m_basepath;
	int m_num_cams;
	int m_begin_frame;
	int m_end_frame;
	int m_files_per_subdir;

public:

	/// file descriptor for MCG dataset image files
	struct _MCG_FD
	{
		int frame;
		unsigned char cam;
		double time;

		_MCG_FD() :
			frame(0),
			cam(0),
			time(0.0)
		{ }

		_MCG_FD(const struct _MCG_FD &fd) :
			frame(fd.frame),
			cam(fd.cam),
			time(fd.time)
		{ }
	};

	typedef struct _MCG_FD MCG_FD;

	MCGDataset(
		const std::string &basepath,
		int begin_frame = 0,
		int end_frame = -1) :
		  m_basepath(basepath),
		  m_begin_frame(begin_frame),
		  m_end_frame(end_frame)
	{
		// Determine number of cameras.
		// We need to temporarily set m_files_per_subdir, but
		// the value should not affect the count for subdir 0.
		m_files_per_subdir = MCG_MAX_FILES_PER_SUBDIR;
		std::vector<fs::path> files_first_frame;
		
		list_imagefiles(files_first_frame, CAM_ALL, 0, 1);
		m_num_cams = files_first_frame.size();

		VBL_ASSERT(m_num_cams > 0, VBL_STR("No images found for dataset " << m_basepath));

		// Compute actual number of files per subdirectory.
		// (Note, this should be integer division)
		m_files_per_subdir = MCG_MAX_FILES_PER_SUBDIR / m_num_cams;
	}

	static
	int count_cam_flags(MCG_CAM_FLAGS cams)
	{
		int cam_count = 0;
		unsigned int cam_bit = 1;

		while(cam_bit < CAM_ALL)
		{
			if (cams & cam_bit)
				++cam_count;

			cam_bit <<= 1;
		}

		return cam_count;
	}

	/// Compute the subdir number for the given frame
	inline 
	int frame_to_subdir(int frame) const
	{
		return frame / m_files_per_subdir; // integer divison
	}

	inline
	int first_frame() const
	{ return m_begin_frame; }

	inline
	fs::path basepath() const
	{ return m_basepath; }

	inline
	std::string subdir_name(int subdir_id) const
	{ 
		return VBL_STR(std::setfill('0') << std::setw(6) << subdir_id);
	}

	inline
	fs::path subdir_path(int subdir_id) const
	{ return m_basepath / subdir_name(subdir_id); }

	inline
	fs::path subdir_path_for_frame(int frame) const
	{ return subdir_path(frame_to_subdir(frame)); }

	inline static
	bool is_image_file(const fs::path &filepath, int &frame, int &cam, double &timestamp)
	{
		return VBL_SCAN(
			filepath.filename(), // remove directory component in string
			"frame" >> frame
			>> "_cam" >> cam
			>> "_ts" >> timestamp
		);
	}

	inline static
	bool is_image_file(const fs::path &filepath, int &frame, int &cam)
	{
		double timestamp;
		return is_image_file(filepath, frame, cam, timestamp);
	}

	inline static
	bool is_image_file(const fs::path &filepath, int &frame)
	{
		int cam;
		return is_image_file(filepath, frame, cam);
	}

	inline static
	bool is_image_file(const fs::path &filepath)
	{
		int frame;
		return is_image_file(filepath, frame);
	}

	inline
	static
	std::string image_filename(int frame, int cam, double timestamp)
	{
		return VBL_STR(
			"frame" << std::setfill('0') << std::setw(6) << frame << 
			"_cam" << cam << 
			"_ts" << std::fixed << std::setprecision(6) << timestamp << 
			".raw");
	}

	inline
	fs::path image_filepath(int frame, int cam, double timestamp)
	{
		return subdir_path_for_frame(frame) / image_filename(frame, cam, timestamp);
	}

	void list_imagefiles(std::vector<fs::path> &files, int cams_flags = CAM_ALL, int begin_frame = -1, int end_frame = -1)
	{
		// use dataset frame limits if begin_frame and/or end_frame are not defined
		if (begin_frame < 0) begin_frame = m_begin_frame;
		if (end_frame < 0) end_frame = m_end_frame;

		int subdir_id = frame_to_subdir(begin_frame);
		int subdir_end = frame_to_subdir(end_frame);

		// iterate over appropriate subdir range
		for(; (end_frame < 0) || (subdir_id <= subdir_end); ++subdir_id)
		{
			fs::path cur_subdir = subdir_path(subdir_id);
			if (!fs::is_directory(cur_subdir)) {
				// no such subdirectory,
				// done
				break;
			}

			// iterate over files in current subdirectory
			fs::directory_iterator f_itr(cur_subdir);
			fs::directory_iterator f_itr_end;
			for(; f_itr != f_itr_end; ++f_itr)
			{
				int img_frame, img_cam;

				if (!is_image_file(f_itr->path(), img_frame, img_cam))
				{
					// skip any possible crud that is not an image file
					continue;
				}

				int img_cam_bit = (1 << img_cam);
				if (!(img_cam_bit & cams_flags))
				{
					// this is not the right camera
					continue;
				}


				if (
					(begin_frame >= 0 && img_frame < begin_frame) ||
					(end_frame >= 0 && img_frame >= end_frame)
				) {
					// we are not within the right frame range
					continue;
				}

				// image file is for a requested camera
				files.push_back(f_itr->path());
			}
		}
	}

};

} // namespace MCG
} // namespace datasets

#endif // MCG_DATASET_H
